#pragma once

#include "PhysicWorld.h"
#include "Physx.h"
#include "PhysxTrigger.h"

class TriggerCallback : public NxUserTriggerReport    
{        
	void onTrigger(NxShape& triggerShape, NxShape& otherShape, NxTriggerFlag status)        
	{   
		if(status & NX_TRIGGER_ON_ENTER)            
		{                
			Trigger* trig = (Trigger*) triggerShape.getActor().userData;
			assert(trig);
			IObject* obj = (IObject*) otherShape.getActor().userData;
			if(obj == NULL) ///maybe TRIGER VS TRIGGER (tem q setar pra isso poder ocorrer)
			{
				Trigger* t2 = (Trigger*) otherShape.getActor().userData;
				assert(t2);						
				trig->TriggerEvent(t2,TriggerFlag::Enter);
			}
			else
			{			
				trig->TriggerEvent(obj,TriggerFlag::Enter);
			}
		}            
		if(status & NX_TRIGGER_ON_STAY)            
		{
			Trigger* trig = (Trigger*) triggerShape.getActor().userData;
			assert(trig);
			IObject* obj = (IObject*) otherShape.getActor().userData;
			if(obj == NULL) ///maybe TRIGER VS TRIGGER (tem q setar pra isso poder ocorrer)
			{
				Trigger* t2 = (Trigger*) otherShape.getActor().userData;
				assert(t2);								
				trig->TriggerEvent(t2,TriggerFlag::In);
			}
			else
			{			
				trig->TriggerEvent(obj,TriggerFlag::In);
			}
		}
		if(status & NX_TRIGGER_ON_LEAVE)            
		{                
			Trigger* trig = (Trigger*) triggerShape.getActor().userData;
			assert(trig);
			IObject* obj = (IObject*) otherShape.getActor().userData;
			if(obj == NULL) ///maybe TRIGER VS TRIGGER (tem q setar pra isso poder ocorrer)
			{
				Trigger* t2 = (Trigger*) otherShape.getActor().userData;
				assert(t2);								
				trig->TriggerEvent(t2,TriggerFlag::Leave);
			}
			else
			{			
				trig->TriggerEvent(obj,TriggerFlag::Leave);
			}
		}            
	}    
};

class MyOutputStream : public NxUserOutputStream
    {
        void reportError (NxErrorCode code, const char *message, const char* file, int line)
        {
            //this should be routed to the application
            //specific error handling. If this gets hit
            //then you are in most cases using the SDK
            //wrong and you need to debug your code!
            //however, code may  just be a warning or
            //information.
        
            if (code < NXE_DB_INFO)
            {
                MessageBox(NULL, message, "SDK Error", MB_OK),
                exit(1);
            }
        }
                
        NxAssertResponse reportAssertViolation (const char *message, const char *file,int line)
        {
            //this should not get hit by
            // a properly debugged SDK!
            assert(0);
            return NX_AR_CONTINUE;
        }
                
        void print (const char *message)
        {
            //printf("SDK says: %s\n", message);
        }

    };



class PhysxWorld : public PhysicWorld
{
public:
	PhysxWorld(void);
	~PhysxWorld(void);

	void Initialization(void);
	void PreUpdate(DWORD timeElapsed) ;
	void PosUpdate(DWORD timeElapsed) ;
	void AddObject(IObject* obj);
	void RemoveObject(IObject* obj);
	void AddTrigger(Trigger* obj) ;
	void RemoveTrigger(Trigger* obj);

	NxScene*				GetCurrentScene() {return scene;}
	NxPhysicsSDK*			GetPhysicsSDK() {return gPhysicsSDK;}
	NxUtilLib*				GetUtilsLib() {return gUtilLib;}
	NxCookingInterface*		GetCookingLib(bool initializeCookingAPI) 
	{
		if(initializeCookingAPI)
		{
			if(CookingInitialized == false)
			{
				gCooking->NxInitCooking();	
				CookingInitialized = true;
			}
		}
		return gCooking;
	}

	NxTriangleMesh* CreateTriangleMesh(NxStream& stream)
	{
		return gPhysicsSDK->createTriangleMesh(stream);
	}
	NxMaterial* CreateMaterial(NxMaterialDesc  mdesc)
	{
		return scene->createMaterial(mdesc); 
	}

	NxHeightField* CreateHeightField(NxHeightFieldDesc hfdesc)
	{
		return gPhysicsSDK->createHeightField(hfdesc);    
	}

private:
	NxPhysicsSDK*			gPhysicsSDK;
	NxCookingInterface*		gCooking ;
	NxUtilLib *				gUtilLib;
	NxScene*				scene;
	MyOutputStream*			myOutputStream;
	bool					CookingInitialized;
	NxControllerManager*	 gManager ;
	TriggerCallback*		callBack;
};